import breve
import random

exit_scale_factor =.6
immobiles = 0

class Controller(breve.Control):
	def __init__(self):
		breve.Control.__init__(self)
		self.setBackgroundColor(breve.vector())
		self.enableLighting()
		self.env = Room(breve.vector(20, 0, 20), 8, [], [], [84, 0])
		self.total_bots = 10
		self.bots_to_exit = 10
		self.tests_to_run = 10
		self.exited_bots = 0
		self.score = 0
		self.start_time = 0
		self.tests_running = False
		print "\n"
			
	def iterate(self):
		if not self.tests_running:
			self.tests_running = True
			self.runTests()
		
		breve.Control.iterate(self)
		self.updateNeighbors()
		
		if self.getTime() - self.start_time > 100:
			self.score += (self.bots_to_exit - self.exited_bots) * 1000
			self.endTest()
			self.runTests()
		elif self.exited_bots == self.bots_to_exit:
			self.endTest()
			self.runTests()
		
	def reset(self):
		# remove all bots
		exitBots = breve.allInstances(ExitBot)
		while len(exitBots) > 0:
			breve.deleteInstance(exitBots.pop())
		blocks = breve.allInstances(breve.Stationary)
		while len(blocks) > 0:
			breve.deleteInstance(blocks.pop())
		
		# reset vars
		self.score = 0
		self.exited_bots = 0
	
	def runTests(self):
		if self.tests_to_run > 0:
			self.tests_to_run -= 1
			self.reset()
			self.startTest()
		else:
			self.total_bots += 10
			self.bots_to_exit += 10
			self.tests_to_run = 10
			self.runTests()
			print "\n"	
						
	def startTest(self):
		self.env.placeBots([ExitBot], [self.total_bots])
		#self.env.placeBots([breve.Stationary], [immobiles])
		self.signalExit()
		self.start_time = self.getTime()

	def endTest(self):
		print self.score/self.bots_to_exit
		
	def signalExit(self):
		exitBots = breve.allInstances(ExitBot)
		for i in range(0, self.bots_to_exit):
			exitBots[i].leaveRoom()

class ExitBot(breve.Mobile):
	def __init__(self):
		breve.Mobile.__init__(self)
		self.enablePhysics()
		# should probably not hardcode this
		self.setNeighborhoodSize(8*1.5)
		self.getOut = False
		self.alive = True
		self.neighborList = []
		self.handleCollisions('ExitBot', 'collide')
		
	def collide(self, obj):
		self.setColor(breve.vector(0,0,0))
		self.alive = False
		if self.getOut:
			self.getOut = False
			self.getController().exited_bots += 1
			self.getController().score += 1000
		if obj.getOut:
			obj.getOut = False
			obj.getController().exited_bots += 1
			obj.getController().score += 1000	
	
	def getNeighbors(self):
		return [x for x in breve.Mobile.getNeighbors(self)
						if not isinstance(x, Obstruction) and x != self]
	
	def iterate(self):
		breve.Mobile.iterate(self)
		self.neighborList = self.getNeighbors()
		vec = breve.randomExpression(breve.vector(2, 0, 2)) - breve.vector(1, 0, 1)
		
		if self.getOut: vec = self.leave()
		else: vec = self.wander()
		
		avoid_vector = breve.vector(0, 0, 0)
		for bot in self.neighborList:
			length = (self.getLocation() - bot.getLocation()).length()
			heading = (self.getLocation() - bot.getLocation()).normalize()				  
			force = 30 /length
			avoid_vector = avoid_vector + heading*force
			
		stop = False
		if self.getOut: 
			for bot in self.neighborList:
				myDistance = (self.exit - self.getLocation()).length();
				botDistance = (self.exit - bot.getLocation()).length();
				if botDistance < myDistance:
					stop = True
					
		if stop: vec = breve.vector(0, 0, 0)	
		else: vec = avoid_vector + vec
		
		if self.alive:
			self.setVelocity(breve.vector(vec[0], 0, vec[2]))
	
	def leave(self):
		return (self.exit - self.getLocation()).normalize() * 7	
	
	def wander(self):
		return (self.getVelocity().normalize()*5 + (breve.randomExpression(
				breve.vector(2, 0, 2)) - breve.vector(1, 0, 1)).normalize())
	
	def leaveRoom(self):	
		self.getOut = True
		self.setColor(breve.vector(1, 0, 0))
					
class Room(breve.Real):
	def __init__(self, dim, unit, botTypes, botCounts, exit):
		breve.Real.__init__(self)
		self.unit = unit
		self.width = int(dim[0])
		self.height = int(dim[2])
		self.exit = breve.vector(exit[0], unit / 2, -exit[1])
		self.getController().pointCamera(
			breve.vector(self.width * unit / 2, 0, -self.height * unit / 2), 
			breve.vector(0, 200, self.height * unit))
		
		for x in range(0, self.width):
			Wall(breve.vector(x * unit + unit / 2, unit / 2, 0), 
				 breve.vector(unit, unit, .1))
			Wall(breve.vector(x * unit + unit / 2, unit / 2, -self.height * unit), 
				 breve.vector(unit, unit, .1))
		
		for z in range(0, self.height):
			Wall(breve.vector(0, unit / 2, -z * unit - unit / 2), 
				 breve.vector(.1, unit, unit))
			Wall(breve.vector(self.width * unit, unit / 2, -z * unit - unit / 2), 
				 breve.vector(.1, unit, unit))
				
		Ground(breve.vector((self.width / 2) * unit, 0, -(self.height / 2) * unit), 
					   breve.vector(self.width * unit, .1, self.height * unit)) 
		
		botCount = 0
		for c in range(0, len(botCounts)):
			botCount += botCounts[c]
		
		if botCount > self.width * self.height:
			print "Too many bots!"
		self.placeBots(botTypes, botCounts)
		
		BlackHole(self.exit, breve.vector(exit_scale_factor*unit, unit, .2))

	def placeBots(self, botTypes, botCounts):
		positions = []
		positionCount = self.width * self.height
		
		for i in range(0, positionCount):
			positions.append(True)

		while len(botCounts) > 0:
			while True:
				spot = random.randint(0, positionCount - 1)
				if positions[spot]: break
				
			positions[spot] = False 
			bot = botTypes[0]()
			bot.move(breve.vector(
				int(spot % self.height) * self.unit + self.unit / 2, self.unit / 2, 
			   -int(spot / self.height) * self.unit - self.unit / 2))
			bot.unit = self.unit
			bot.setShape(breve.createInstances(breve.Cube, 1).initWith(
				breve.vector(self.unit - 1, self.unit - 1, self.unit - 1)))
			
			if isinstance(bot, ExitBot):
				bot.exit = self.exit
				bot.setShape(breve.createInstances(
					breve.Sphere, 1).initWith(self.unit / 2 - 1))
			
			botCounts[0] -= 1
			if botCounts[0] == 0:
				botCounts.pop(0)
				botTypes.pop(0)

class Obstruction(breve.Stationary):
	def __init__(self, position, dimension, color):
		breve.Stationary.__init__(self)
		self.setTexture(-1)
		self.setShape(breve.createInstances(breve.Cube, 1).initWith(dimension))
		self.setColor(color)
		self.move(position)
	
class Wall(Obstruction):
	def __init__(self, position, dimension):
		Obstruction.__init__(self, position, dimension, breve.vector(1.0, 1.0, 1.0))
		#self.setBitmapTransparency(0.5)	
		
class Ground(Obstruction):
	def __init__(self, position, dimension):
		Obstruction.__init__(self, position, dimension, breve.vector(0.0, 0.3, 0.0))
		#self.setBitmapTransparency(0.5)
		
class BlackHole(Obstruction):
	def __init__(self, position, dimension):
		Obstruction.__init__(self, position, dimension, breve.vector(0, 0, 0))
		self.setNeighborhoodSize(4)
		self.handleCollisions('ExitBot', 'collide')
		
	def collide(self, x):
		if isinstance(x, ExitBot) and x.getOut:
			controller = self.getController()
			controller.exited_bots += 1
			controller.score += controller.getTime() - controller.start_time 
			breve.deleteInstance(x)
		elif isinstance(x, Wall):
			breve.deleteInstance(x)
				
				
		
Controller()
